﻿using System.Threading;
using System;

namespace Framework
{
    public class Channel<T>
    {
        private int _capacity;
        private int _realCapacity;
        const int DEFALUT_REAL_CAPACITY = 100;
        private T[] _ringQueue;
        private int _enequeIndex;
        private int _dequeuIndex;
        private Semaphore _semaphoreEmpty;
        private Semaphore _semaphoreFull;
        
        public Channel(int capacity)
        {
            _capacity = capacity;
            _realCapacity = Math.Min(DEFALUT_REAL_CAPACITY, _capacity);
            _ringQueue = new T[_realCapacity];
            _enequeIndex = 0;
            _dequeuIndex = 0;
            _semaphoreEmpty = new Semaphore(0, capacity);
            _semaphoreFull = new Semaphore(capacity, capacity);
        }

        ~Channel()
        {
            _semaphoreEmpty.Close();
            _semaphoreFull.Close();
        }

        public void Enqueue(T item)
        {
            _semaphoreFull.WaitOne();
            lock (_ringQueue)
            {
                if(_enequeIndex == _realCapacity)
                {
                    T[] newRingQueue = new T[_realCapacity * 2];
                    Array.Copy(_ringQueue, newRingQueue, _realCapacity);
                    _realCapacity = _realCapacity * 2;
                    _ringQueue = newRingQueue;
                }
                _ringQueue[_enequeIndex] = item;
            }
            _semaphoreEmpty.Release();
            _enequeIndex = (_enequeIndex + 1) % _capacity;
        }

        public T Dequeue()
        {
            _semaphoreEmpty.WaitOne();
            T item;
            lock (_ringQueue)
            {
                item = _ringQueue[_dequeuIndex];
            }
            _semaphoreFull.Release();
            _dequeuIndex = (_dequeuIndex + 1) % _capacity;
            return item;
        }
    }
}

